﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Collections.Specialized;
using System.Web;

namespace Tilver.Web
{
    /// <summary>
    /// HTTP的POST请求类
    /// 用于POST到URL并获得返回结果
    /// </summary>
    public class WebPostRequest
    {
        /// <summary>
        /// 访问的URL
        /// </summary>
        public string Url { get; internal set; }

        /// <summary>
        /// 访问URL的字符集
        /// </summary>
        public Encoding Encoding { get; internal set; }

        /// <summary>
        /// 访问时包含的Cookie
        /// </summary>
        public CookieContainer Cookie { get; internal set; }

        /// <summary>
        /// 提交到URL的数据
        /// </summary>
        public NameValueCollection PostData { get; internal set; }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="url">访问的URL</param>
        /// <param name="encoding">访问URL的字符集</param>
        /// <param name="cookie">访问时包含的Cookie</param>
        public WebPostRequest(string url, Encoding encoding = null, CookieContainer cookie = null)
        {
            Url = url;
            Encoding = encoding ?? Encoding.UTF8;
            Cookie = cookie;
            PostData = new NameValueCollection();
        }

        /// <summary>
        /// 如URL是AspNet页面则POST时需要添加AspNet相关的信息
        /// 包括ViewState和EventValidation
        /// </summary>
        /// <param name="url">获取AspNet数据的页面</param>
        public void AddAspNetData(string url = null)
        {
            url = url ?? Url;
            WebGetRequest request = new WebGetRequest(url, Encoding, Cookie);
            string html = request.GetResponse();
            PostData.Add("__VIEWSTATE", GetViewStateFromHtml(html));
            PostData.Add("__EVENTVALIDATION", GetEventValidationFromHtml(html));
        }

        /// <summary>
        /// 添加表单信息
        /// 如果是ASP.NET服务器控件提交的，需要提供控件ID和Text
        /// </summary>
        /// <param name="name"></param>
        /// <param name="value"></param>
        public void AddFormData(string name, string value)
        {
            PostData.Add(name, value);
        }

        /// <summary>
        /// 提交页面并获取返回
        /// </summary>
        /// <returns></returns>
        public string GetResponse()
        {
            using (Stream stream = GetResponseStream())
            {
                using (StreamReader reader = new StreamReader(stream, Encoding))
                {
                    return reader.ReadToEnd();
                }
            }
        }

        /// <summary>
        /// 提交页面并获取返回数据流
        /// </summary>
        /// <returns></returns>
        public Stream GetResponseStream()
        {
            byte[] postData = GetPostData();

            HttpWebRequest request = WebRequest.Create(Url) as HttpWebRequest;
            request.Method = "POST";
            request.KeepAlive = false;
            request.CookieContainer = Cookie;
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = postData.Length;
            //写入请求数据
            using (Stream requestStream = request.GetRequestStream())
            {
                requestStream.Write(postData, 0, postData.Length);
            }

            HttpWebResponse response = request.GetResponse() as HttpWebResponse;
            return response.GetResponseStream();
        }

        //获得Post数据
        private byte[] GetPostData()
        {
            StringBuilder ret = new StringBuilder();
            for (int i = 0; i < PostData.Count; i++)
            {
                string name = PostData.GetKey(i);
                string value = HttpUtility.UrlEncode(PostData.Get(i), Encoding);
                ret.Append(name + "=" + value ?? string.Empty);
                if (i < PostData.Count - 1)
                    ret.Append("&");
            }

            return Encoding.GetBytes(ret.ToString());
        }

        #region Utils
        private const string _viewStateFlag = "id=\"__VIEWSTATE\" value=\"";
        private const string _eventValidationFlag = "id=\"__EVENTVALIDATION\" value=\"";

        // 获取AspNet页面中的ViewState
        private string GetViewStateFromHtml(string html)
        {
            return GetStringFromAspNetHtml(html, _viewStateFlag);
        }

        // 获取AspNet页面中的EventValidation
        private string GetEventValidationFromHtml(string html)
        {
            return GetStringFromAspNetHtml(html, _eventValidationFlag);
        }
        private string GetStringFromAspNetHtml(string html, string flag)
        {
            string ret = string.Empty;
            int start = html.IndexOf(flag);
            if (start >= 0)
            {
                start += flag.Length;
                int end = html.IndexOf("\"", start) - start;
                ret = html.Substring(start, end);
            }
            return ret;
        }
        #endregion
    }
}
